{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Variational Quantum Eigensolver (VQE) with Classiq\n",
    "\n",
    "The variational quantum eigensolver (VQE) is arguably the most common variational quantum algorithm. It combines quantum and classical techniques to solve optimization problems from industries such as finance, logistics, and chemistry. VQE works because of the variational principle, which defines the relationship between the lowest energy of a system and the expectation value of a given state, declaring a lower bound to the expectation value.\n",
    "\n",
    "We are going to create a VQE algorithm to estimate the minimum energy(eigenvalue) of the following Hamiltonian\n",
    "$$Z+0.1X$$ \n",
    "where $Z$ and $X$ are Pauli Operators and the ansatz we take is a single qubit $RY$ gate "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "from classiq import *\n",
    "\n",
    "#Defining the Hamiltonian from the problem\n",
    "HAMILTONIAN = QConstant(\"HAMILTONIAN\", List[PauliTerm], \n",
    "                        [            \n",
    "            PauliTerm([Pauli.Z], 1),\n",
    "            PauliTerm([Pauli.X], 0.1)])\n",
    "\n",
    "#Defining the Ansatz for the Problem\n",
    "@qfunc\n",
    "def main(q: Output[QBit], angles: CArray[CReal, 1]) -> None:\n",
    "    allocate(1, q)\n",
    "    RY(angles[0], q) \n",
    "\n",
    "#Defining the Variational Quantum Eigensolver primitives with proper paramters\n",
    "@cfunc\n",
    "def cmain() -> None:\n",
    "    res = vqe(\n",
    "        HAMILTONIAN, #Hamiltonian of the problem\n",
    "        False, #Maximize Parameter\n",
    "        [],\n",
    "        optimizer=Optimizer.COBYLA, # Classical Optimizer\n",
    "        max_iteration=1000,\n",
    "        tolerance=0.001,\n",
    "        step_size=0,\n",
    "        skip_compute_variance=False,\n",
    "        alpha_cvar=1.0,\n",
    "    )\n",
    "    save({\"result\": res})\n",
    "\n",
    "qmod = create_model(main, classical_execution_function=cmain)\n",
    "qprog = synthesize(qmod)\n",
    "write_qmod(qmod, name=\"vqe_primitives\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now model and get the results for the created vqe primitive:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/8758a1e5-bd68-4be4-896e-7fd4e98d9a95?version=0.42.0\n"
     ]
    }
   ],
   "source": [
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The list of other optimizers can be found here: [Link](https://docs.classiq.io/latest/reference-manual/built-in-algorithms/ground-state-solving/advanced-usage/solver-customization/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "estimation = execute(qprog)\n",
    "# res.open_in_ide()\n",
    "vqe_result = estimation.result()[0].value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Minimal energy of the Hamiltonian -1.015625\n",
      "Optimal parameters for the Ansatz {'angles_0': 3.2430102953668842}\n"
     ]
    }
   ],
   "source": [
    "print(\"Minimal energy of the Hamiltonian\", vqe_result.energy)\n",
    "print(\"Optimal parameters for the Ansatz\", vqe_result.optimal_parameters)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Mathematical Background"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To find the minimal energy of the Hamiltonian \n",
    "$H = Z + 0.1X$ \n",
    "using the Variational Quantum Eigensolver (VQE) with an ansatz that consists of a simple \n",
    "$RY$ \n",
    "gate, we can follow these steps:\n",
    "\n",
    "__First we define the Hamiltonian__:<br>\n",
    "The Hamiltonian is given by:\n",
    "\n",
    "$$\n",
    "H = Z + 0.1X\n",
    "$$\n",
    "\n",
    "where \n",
    "$Z$ \n",
    "and \n",
    "$X$ \n",
    "are the Pauli-Z and Pauli-X operators, respectively.\n",
    "\n",
    "__We can write the given Pauli matrices:__<br>\n",
    "The Pauli-Z and Pauli-X matrices are:\n",
    "\n",
    "$$\n",
    "Z = \\begin{pmatrix} 1 & 0 \\\\ 0 & -1 \\end{pmatrix}, \\quad X = \\begin{pmatrix} 0 & 1 \\\\ 1 & 0 \\end{pmatrix}\n",
    "$$\n",
    "\n",
    "__Taking the Ansatz__:<br>\n",
    "We use a single \n",
    "$RY$ \n",
    "gate as the ansatz. The \n",
    "$RY(\\theta)$ \n",
    "gate is defined as:\n",
    "\n",
    "$$\n",
    "RY(\\theta) = \\begin{pmatrix} \\cos\\left(\\frac{\\theta}{2}\\right) & -\\sin\\left(\\frac{\\theta}{2}\\right) \\\\ \\sin\\left(\\frac{\\theta}{2}\\right) & \\cos\\left(\\frac{\\theta}{2}\\right) \\end{pmatrix}\n",
    "$$\n",
    "\n",
    "The ansatz state can be written as:\n",
    "\n",
    "$$\n",
    "|\\psi(\\theta)\\rangle = RY(\\theta)|0\\rangle\n",
    "$$\n",
    "\n",
    "We now proceed to the expectation value calculation:<br>\n",
    "The goal is to find the expectation value of the Hamiltonian \n",
    "$H$ \n",
    "with respect to the ansatz state \n",
    "$|\\psi(\\theta)\\rangle$:\n",
    "\n",
    "$$\n",
    "E(\\theta) = \\langle \\psi(\\theta) | H | \\psi(\\theta) \\rangle\n",
    "$$\n",
    "\n",
    "This involves calculating the expectation values of \n",
    "$Z$ \n",
    "and \n",
    "$X$ \n",
    "with respect to \n",
    "$|\\psi(\\theta)\\rangle$.\n",
    "\n",
    "__Calculations can be proceed__: <br>\n",
    "The expectation value of \n",
    "$Z$ \n",
    "with respect to \n",
    "$|\\psi(\\theta)\\rangle$ \n",
    "is:\n",
    "\n",
    "$$\n",
    "\\langle \\psi(\\theta) | Z | \\psi(\\theta) \\rangle = \\cos(\\theta)\n",
    "$$\n",
    "\n",
    "The expectation value of \n",
    "$X$ \n",
    "with respect to \n",
    "$|\\psi(\\theta)\\rangle$ \n",
    "is:\n",
    "\n",
    "$$\n",
    "\\langle \\psi(\\theta) | X | \\psi(\\theta) \\rangle = \\sin(\\theta)\n",
    "$$\n",
    "\n",
    "Therefore, the total expectation value is:\n",
    "\n",
    "$$\n",
    "E(\\theta) = \\cos(\\theta) + 0.1\\sin(\\theta)\n",
    "$$\n",
    "\n",
    "__Minimizing the Energy__:<br>\n",
    "To find the minimal energy, we need to minimize \n",
    "$E(\\theta)$ \n",
    "with respect to \n",
    "$\\theta$. \n",
    "This can be done by solving the equation:\n",
    "\n",
    "$$\n",
    "\\frac{dE(\\theta)}{d\\theta} = -\\sin(\\theta) + 0.1\\cos(\\theta) = 0\n",
    "$$\n",
    "\n",
    "Solving this gives:\n",
    "\n",
    "$$\n",
    "\\tan(\\theta) = 0.1\n",
    "$$\n",
    "\n",
    "$$\n",
    "\\theta = \\tan^{-1}(0.1)\n",
    "$$\n",
    "\n",
    "__Minimal Energy__:<br>\n",
    "Substitute \n",
    "$\\theta$ \n",
    "back into the expression for \n",
    "$E(\\theta)$:\n",
    "\n",
    "$$\n",
    "E_{\\text{min}} = \\cos(\\tan^{-1}(0.1)) + 0.1\\sin(\\tan^{-1}(0.1))\n",
    "$$\n",
    "\n",
    "Using the identities:\n",
    "\n",
    "$$\n",
    "\\cos(\\tan^{-1}(x)) = \\frac{1}{\\sqrt{1+x^2}}, \\quad \\sin(\\tan^{-1}(x)) = \\frac{x}{\\sqrt{1+x^2}}\n",
    "$$\n",
    "\n",
    "For \n",
    "$x = 0.1$:\n",
    "\n",
    "$$\n",
    "E_{\\text{min}} = \\frac{1}{\\sqrt{1+0.01}} + 0.1 \\cdot \\frac{0.1}{\\sqrt{1+0.01}} = \\frac{1}{\\sqrt{1.01}} + \\frac{0.01}{\\sqrt{1.01}} = \\frac{1.01}{\\sqrt{1.01}} = \\sqrt{1.01}\n",
    "$$\n",
    "\n",
    "Thus, the minimal energy is:\n",
    "\n",
    "$$\n",
    "E_{\\text{min}} = \\sqrt{1.01}\n",
    "$$\n",
    "\n",
    "However, considering that the eigenvalues of \n",
    "$Z$ \n",
    "are \n",
    "$\\pm 1$, \n",
    "the minimal energy will actually be:\n",
    "\n",
    "$$\n",
    "E_{\\text{min}} = -\\sqrt{1.01}\n",
    "$$\n",
    "\n",
    "Thus, the minimal energy is:\n",
    "\n",
    "$$\n",
    "E_{\\text{min}} = -\\sqrt{1.01} \\approx -1.004987562112089\n",
    "$$\n",
    "\n",
    "__It is very similar to the result which we observed based on the VQE primitive in the Classiq simulator.__\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
